#include <stdlib.h>
#include "R250_521.h"

namespace game
{
    R250_521::R250_521(){
        r250_index = new int;
        r521_index = new int;
        r250_buffer = new unsigned long [r250_length];
        r521_buffer = new unsigned long [r521_length];
        
        int i = r521_length;
        unsigned long mask1 = 0;
        unsigned long mask2 = 0xFFFFFFFF;
        
        
        while(i-- > r250_length){
            r521_buffer[i] = rand();
        }
        while(i-- > 31){
            r250_buffer[i] = rand();
            r521_buffer[i] = rand();
        }
        
        /*
        Establish linear independence of the bit columns
        by setting the diagonal bits and clearing all bits above
        */
        while (i-- > 0) {
            r250_buffer[i] = (rand() | mask1) & mask2;
            r521_buffer[i] = (rand() | mask1) & mask2;
            mask2 ^= mask1;
            mask1 >>= 1;
        }
        r250_buffer[0] = mask1;
        r521_buffer[0] = mask2;
        *r250_index = 0;
        *r521_index = 0;
    }
    
    R250_521::~R250_521(){
        delete r250_index;
        delete r521_index;
        delete [] r250_buffer;
        delete [] r521_buffer;
    }
    
    unsigned long R250_521::next(){
        /*
        I prescale the indices by sizeof(unsigned long) to eliminate
        four shlwi instructions in the compiled code.  This minor optimization
        increased perf by about 12%.
        
        I also carefully arrange index increments and comparisons to minimize
        instructions.  gcc 3.3 seems a bit weak on instruction reordering. The
        j1/j2 branches are mispredicted, but nevertheless these optimizations
        increased perf by another 10%.
        */
        
        int i1 = *r250_index;
        int i2 = *r521_index;
        unsigned char * b1 = (unsigned char*)r250_buffer;
        unsigned char * b2 = (unsigned char*)r521_buffer;
        unsigned long * tmp1, * tmp2;
        unsigned long r, s;
        int j1, j2;
        
        j1 = i1 - R250_IB();
        if (j1 < 0)
            j1 = i1 + R250_IA();
        j2 = i2 - R521_IB();
        if (j2 < 0)
            j2 = i2 + R521_IA();
        
        tmp1 = (unsigned long *)(b1 + i1);
        r = (*(unsigned long *)(b1 + j1)) ^ (*tmp1);
        *tmp1 = r;
        tmp2 = (unsigned long *)(b2 + i2);
        s = (*(unsigned long *)(b2 + j2)) ^ (*tmp2);
        *tmp2 = s;
        
        i1 = (i1 != sizeof(unsigned long)*(r250_length-1)) ? (i1 + sizeof(unsigned long)) : 0;
        *r250_index = i1;
        i2 = (i2 != sizeof(unsigned long)*(r521_length-1)) ? (i2 + sizeof(unsigned long)) : 0;
        *r521_index = i2;
            
        return r ^ s;
    }
}
